﻿
/*
    Questo oggetto incapsula le informazioni associate ad una tratta di un viaggio.
    Corrisponde ad una estensione di un GeoRoute lato server. I parametri del costruttore sono:
    - route = risultato dell'eval della stringa json di un GeoRoute
    - encodedPolyline = polyline codificata relativa alla tratta
    - isEditable = flag che indica se la tratta fa parte di un viaggio editabile oppure no
    - isNotEncoded = parametro opzionale. Se settato a true indica che il parametro 
        <encodedPolyline> non rappresenta una GPolyline codificata ma una lista di GLatLng
        che identifica la polilinea.
*/
MapControl2.AAARoute = function(route, encodedPolyline, isEditable, isNotEncoded, map)
{
    this._routeModel = route;
    this._isEditable = isEditable;
    this._parentTrip = null;
    this._map = map;
    this._waypointMarkers = new Array();
    this._polylineColor = MapControl2.AAARoute._routeColor[this._routeModel.vehicle];
    this._createWaypoints();
    
    this._errAnimeObj = this._createErrAnimeObj();
    this._clickAnimeObj = this._createClickAnimeObj();
    
    if(encodedPolyline != null)
    {
        if (isNotEncoded)
        {
            if (_googleEncPol == null)
                        _initializeGPolylineDescriptorField(encodedPolyline);
        }
        this._createPolyline(encodedPolyline, !isNotEncoded);
        this._map.addOverlay(this._polyline);
        this._status = AAASTATUS_OK;
    }
    else
    {    
        this._status = AAASTATUS_ERROR;
        this._createErrorPolyline();
    }
    this._polyline.registerWaypoints(this._waypointMarkers, true);
    
    this._gDir = new google.maps.Directions();
};

MapControl2.AAARoute.prototype = {

    // ---------------------- private functions ----------------------------------------------------
    _createErrAnimeObj: function()
    {
        var o = new Object();
        o.pol = null;
        o.molt = -1;
        o.stop = true;
        o.op = 1;
        o.initialize = function(polyline){this.op = 1; this.pol = polyline; this.stop = false; this.molt = -1;};
        o.DoAction = function(d) 
                     {
                        this.pol.setStrokeStyle({opacity: this.op});
                        this.op += (this.molt * 0.1);
                        if (this.op >= 1) this.molt = -1;
                        if (this.op <= 0.2) this.molt = 1;
                        if (!this.stop)
                        {
                            AnimeGlobalTimer.SetAlertMillis(this, 100);
                        }
                     };
        o.Stop = function() {this.stop = true;};
        o.isRunning = function() {return (!this.stop);};
        return o;
    },
    
    _createClickAnimeObj: function()
    {
        var o = new Object();
        o.pol = null;
        o.stop = true;
        o.weight = 5;
        o.down = true;
        o.initialize = function(polyline)
                       {this.weight = 5; this.down = true; this.pol = polyline; this.stop = false;};
        o.DoAction = function(d) 
                     {
                        this.pol.setStrokeStyle({weight: this.weight});
                        this.down? this.weight-- : this.weight++;
                        if(this.weight == 1) this.down = false;
                        if(this.weight == 8) this.down = true; 
                        if (!this.stop)
                        {
                            AnimeGlobalTimer.SetAlertMillis(this, 100);
                        }
                     };
        o.Stop = function() {this.stop = true;};
        o.isRunning = function() {return (!this.stop);};
        return o;
    },
    
    _createPolyline: function(gpolyline, isEncoded)
    {
        if(this._polyline != null)
        {
            google.maps.Event.clearInstanceListeners(this._polyline);
            this._parentTrip._parentMap.removeOverlay(this._polyline);                
        }
        
        switch(this._routeModel.vehicle)
        {
            case 0: // car
            case 1: // motorbike
            case 2: // bike
                if (isEncoded)
                {
                    gpolyline.color = this._polylineColor;
                    gpolyline.opacity = 0.65;
                    this._polyline = new google.maps.Polyline.fromEncoded(gpolyline);
                    if (_googleEncPol == null)
                        _initializeGPolylineDescriptorField(this._polyline);
                }else
                {
                    if (_googleEncPol == null)
                        _initializeGPolylineDescriptorField(gpolyline);
                    var polyline_descriptor = new Object();
                    polyline_descriptor = new Object();
                    polyline_descriptor.numLevels = 4;
                    polyline_descriptor.zoomFactor = 16;
                    polyline_descriptor.opacity = 0.65;
                    polyline_descriptor.color = this._polylineColor;
                    
                    //polyline_descriptor.points = MapControl2.AAAUtils.encodeGPolyline(gpolyline);
                    
                    polyline_descriptor.points = gpolyline[_googleEncPol].points;
                    polyline_descriptor.levels = gpolyline[_googleEncPol].levels;
                    this._polyline = new google.maps.Polyline.fromEncoded(polyline_descriptor);
                }
            break;
            default: // train, ship, airplane
                if (!isEncoded)
                {
                    if (_googleEncPol == null)
                        _initializeGPolylineDescriptorField(gpolyline);
                    var latlngs = this._getGLatLngArray();
                    this._polyline = new google.maps.Polyline(latlngs, this._polylineColor, 5, 0.65);
                    this._polyline[_googleEncPol] = createEncodings(latlngs);
                }else
                {
                    gpolyline.opacity = 0.65;
                    gpolyline.color = this._polylineColor;
                    this._polyline = new google.maps.Polyline.fromEncoded(gpolyline);
                    if (_googleEncPol == null)
                        _initializeGPolylineDescriptorField(this._polyline); 
                }
            break;
        }
        
        if(this._parentTrip != null)
            this.setDeviationRadius(this._parentTrip._tripModel.deviationRadius/1000);
        
        // event bindings
        if (this._isEditable){
            
            GEvent.bind(this._polyline, 'polyclick', this, this._polyClick);
        }
    },
    
    _createWaypoints: function()
    {
        var current_waypoint = null;
        
        if (this._routeModel.viaPoints !== null && this._routeModel.viaPoints.length > 0)
        {
            var i = 0;
            var waypoint_marker = null;
            for(i = 0; i<this._routeModel.viaPoints.length; i++)
            {
                current_waypoint = this._routeModel.viaPoints[i];
                waypoint_marker = MapControl2.AAAMarker.createMarker
                    (current_waypoint, !this._isEditable, MapControl2.AAAMarker.WAYPOINT_MARKER, '');
                    
                google.maps.Event.bind(waypoint_marker, 'dragend2', this, this._waypointDragEnd);
                this._waypointMarkers.push(waypoint_marker);
                this._map.addOverlay(waypoint_marker);
            }
        }
    },
    
    _createErrorPolyline: function()
    {
        var latlngs = this._getGLatLngArray();
        var err_poly = new google.maps.Polyline(latlngs, "#FF0000", 5, 1, {clickable: false});
        if (this._polyline != null)
        {
            this._map.removeOverlay(this._polyline);
        }
        this._polyline = err_poly;
        this._map.addOverlay(this._polyline);
      
        if (!this._errAnimeObj.isRunning())
        {
            this._errAnimeObj.initialize(this._polyline);  
            AnimeGlobalTimer.SetAlertMillis(this._errAnimeObj, 100);
        }else
        {
            this._errAnimeObj.stop = false;
            this._errAnimeObj.pol = this._polyline;
        }
    },
    
    _getGLatLngArray: function()
    {
        var startGeoloc = this._routeModel.start;
        var endGeoloc = this._routeModel.end;
        var toRet = new Array();
        
        toRet.push(MapControl2.AAAUtils.toGLatLng(startGeoloc));
        var i = 0;
        for (i = 0; i<this._routeModel.viaPoints.length; i++)
        {
            toRet.push(MapControl2.AAAUtils.toGLatLng(this._routeModel.viaPoints[i]));
        }
        toRet.push(MapControl2.AAAUtils.toGLatLng(endGeoloc));
        return toRet;
    },
    // -----------------------------------------------------------------------------------------
    
    // -------------- event handlers -----------------------------------------------------------
    _polyClick: function(poly, polylatlng, mousePos, rightClick, subrouteIndex)
    {   
        GEvent.trigger(this, 'route_click', this, subrouteIndex, rightClick, polylatlng, mousePos);
    },

    _waypointDragEnd: function(waypointMarker)
    {   
        var map = this.getParentTrip().getParentMap();
           
        /*
        // testo se il waypoint rientra ne radius dei passeggeri
        var passengerPoint = null;
        if(map._passengerDep != null )
            if( map._passengerDep.isInRadius(waypointMarker.getLatLng()) )
            //if(map._passengerDep._initPosition.distanceFrom(waypointMarker.getLatLng()) < map._passengerDep._radiusKm*1000.0 )
                passengerPoint = map._passengerDep;
        
        if(map._passengerArr != null)
            if( map._passengerArr.isInRadius(waypointMarker.getLatLng()) )
            //if(map._passengerArr._initPosition.distanceFrom(waypointMarker.getLatLng()) < map._passengerArr._radiusKm*1000.0 )
                passengerPoint = map._passengerArr;
        
        var attached = false;
        if(passengerPoint != null && map._isEditable)
        {
            if(map._editableTrip != null)
                attached = passengerPoint.attachToTrip(waypointMarker);
        }
        */
       
        var me = this;
        this.refreshPolyline( function(){GEvent.trigger(me, 'route_changed', me)}, 
                              function(){GEvent.trigger(me, 'route_error', me);} );
    },
    
    _waypointRemove: function(waypoint)
    {
        google.maps.Event.clearInstanceListeners(waypoint);
        if (this._routeModel.viaPoints.length == 1)
        {
            this._routeModel.viaPoints.shift();
            this._waypointMarkers.shift();
        }else
        {
            var newWaypointMarkers = new Array(), newViaPoints = new Array();
            var i = 0, currentWaypoint = null;
            for (i = 0; i<this._waypointMarkers.length; i++)
            {
                currentWaypoint = this._waypointMarkers[i];
                if (currentWaypoint !== waypoint)
                {
                    newWaypointMarkers.push(currentWaypoint);
                    newViaPoints.push(currentWaypoint.getGeoLoc());
                }
            }
            this._routeModel.viaPoints = newViaPoints;
            this._waypointMarkers = newWaypointMarkers;
        }
        
        this.refreshPolyline(Function.createDelegate(this, ok_callback));
        
        function ok_callback()
        {
            this._parentTrip._parentMap.removeOverlay(waypoint);
            google.maps.Event.trigger(this, 'route_changed', this);
        }
    },
    // ---------------------------------------------------------------------------------------------
    
    // -------------------- properties -------------------------------------------------------------
    getPolyline: function()
    {
        return (this._polyline);
    },
    
    setPolyline: function(value)
    {
        this._createPolyline(value);
    },
    
    getRouteModel: function()
    {
        return (this._routeModel);
    },
    
    getWaypoints: function()
    {
        return (this._waypointMarkers);
    },
    
    getParentTrip: function()
    {
        return (this._parentTrip);
    },
    
    setParentTrip: function(value)
    {
        this._parentTrip = value;
        this.setDeviationRadius(value._tripModel.deviationRadius/1000);
        google.maps.Event.bind(this, 'route_changed', value, value._routeChanged);
        google.maps.Event.bind(this, 'route_error', value, value._routeError);   
        google.maps.Event.bind(this, 'route_click', value, value._routeClick);           
    },
    
    getStatus : function()
    {
        return this._status;
    },
    
    isEditable: function()
    {
        return (this._isEditable);
    },
    // --------------------------------------------------------------------------------------------
   
    // -------------------------- public functions ------------------------------------------------
    // rende l'oggetto "visitabile"
    accept: function(visitor)
    {
        visitor.visit(this, MapControl2.AAAROUTE_ELEMENT);
    },
    
    insertWaypoint: function(pos, geoloc)
    {   
        if(pos < 0 || pos > this._waypointMarkers.length)
            return;
        
        var vps = this._routeModel.viaPoints;
        if (pos == vps.length)
        {
            vps.push(geoloc);
        }else
        {
            var new_vps = new Array();
            for (var i = 0; i<vps.length; i++)
            {
                if (i == pos)
                {
                    new_vps.push(geoloc);
                }
                new_vps.push(vps[i]);
            }
            this._routeModel.viaPoints = new_vps;
        }
        
        for(var i = 0; i<this._waypointMarkers.length; i++)
        {
            this._parentTrip._parentMap.removeOverlay(this._waypointMarkers[i]);
        }
        this._waypointMarkers = new Array();
        this._createWaypoints();
        this.refreshPolyline(Function.createDelegate(this, f));
        function f()
        {
            google.maps.Event.trigger(this, 'route_changed', this);
        };
    },
       
    
    refreshPolyline: function(ok_callback, err_callback)
    {
        if (this._errAnimeObj.isRunning()) this._errAnimeObj.Stop();
        if (this._clickAnimeObj.isRunning()) this._clickAnimeObj.Stop();
    
        // devo aggiornare la polilinea
        var glatlngs = this._getGLatLngArray();
        var tmpNet = new MapControl2.AAANetUtils();
        
        // faccio la richiesta a google
        this._status = AAASTATUS_LOADING;
        tmpNet.performGoogleRequest(this._gDir, glatlngs, 
            Function.createDelegate(this, gDirectionsLoaded), 
            Function.createDelegate(this, gDirectionError));
        
        // questa funzione viene richiamata se la GDirections viene caricata correttamente
        function gDirectionsLoaded()
        {   
            // nel qual caso aggiorno i dati della route e ridisegno la nuova polyline
            MapControl2.AAARoute.updateRouteModelFromGDirections(this._routeModel, this._gDir);
            this._createPolyline(this._gDir.getPolyline());
            this._parentTrip._parentMap.addOverlay(this._polyline);
            
            // si riposizionano i marker sul percorso
            for (var j=0;j<this._gDir.getNumRoutes()-1;j++)
		    {
			    var mm = this._gDir.getMarker(j+1);
			    var vm = this._waypointMarkers[j];
			    vm.setLatLng(mm.getLatLng());			   
		    }       
		       
		    this._polyline.registerWaypoints(this._waypointMarkers, false);
            ok_callback();
            this._status = AAASTATUS_OK;
        }
        
        // questa funzione viene chiamata quando si è verificato un errore
        function gDirectionError(err)
        {   
            // se c'era una polyline viene cancellata
            if(this._polyline != null)
            {
                google.maps.Event.clearInstanceListeners(this._polyline);
                this._parentTrip._parentMap.removeOverlay(this._polyline);
                this._polyline = null;
            }
                       
            // viene disegnata una polyline fittizia e richiamata la callback di errore
            this._createErrorPolyline();
            this._status = AAASTATUS_ERROR;
            
            if (err_callback)
            {
                err_callback(err);
            }
        }
    },
    
    refreshHandlersAndPolyline: function(okCallback)
    {
        for (var i = 0; i<this._waypointMarkers.length; i++)
        {
            google.maps.Event.clearListeners(this._waypointMarkers[i], 'dragend2');
            google.maps.Event.bind(this._waypointMarkers[i], 'dragend2', this, this._waypointDragEnd);
        }
        
        this.refreshPolyline(refreshPolylineCallback);
        function refreshPolylineCallback()
        {
            okCallback();
        };
    },
    
    clear: function()
    {
        google.maps.Event.clearInstanceListeners(this);
        if(this._polyline != null)
        {
            google.maps.Event.clearInstanceListeners(this._polyline);
            this._map.removeOverlay(this._polyline);
        }    
        
        for(var m=0; m<this._waypointMarkers.length; m++)
        {
            google.maps.Event.clearInstanceListeners(this._waypointMarkers[m]);
            this._map.removeOverlay(this._waypointMarkers[m]);
        }        
    },
    
    setDeviationRadius: function(kms){
        if (this._polyline != null){
            this._polyline.setDeviationRadius(kms);
        }
    },
    
    isInDeviation: function(latlng){
        if (this._status == AAASTATUS_ERROR)
            return {'result': false, 'deviation':-1};
        return (this._polyline.isInDeviation(latlng));
    },
    
    getStart: function(){
        return this._markers[0].getGeoLoc();
    },
    
    getEnd: function(){
        return (this._markers[this._markers.length - 1].getGeoLoc());
    },
    
    setVehicle: function(v){
        if (this._isEditable){
            this._routeModel.vehicle = v.Class;
            this._routeModel.vehicleId = v.Id;
            this._polylineColor = MapControl2.AAARoute._routeColor[v.Class];
            var me = this;
            this.refreshPolyline(function ok(){GEvent.trigger(me, 'route_changed', me);}, 
                                 function err(e){GEvent.trigger(me, 'route_error', me, e);});
        }
    }
    // --------------------------------------------------------------------------------------------
};

// tabulazione della funzione veicoli -> colori
MapControl2.AAARoute._routeColor = new Array(
    "#FF00FF",  // car 
    "#FF00FF",  // bike
    "#FF00FF",  // motorbike
    "#800000",  // train
    "#000080",  // ship
    "#87CEEB"   // airplane
);

// ------------- routine di creazione di una AAARoute da un oggetto GDirections -------------------------
MapControl2.AAARoute.createRouteFromGDirections = function(locationString, locationPoint, gDirections, map, vehicle) 
{       
        // costruisco i geoloc di start, stop e waypoints
        var geolocs = new Array();
        for (i = 0; i<locationString.length; i++)
        {
            var current_geoloc = new Object();
            current_geoloc.address = locationString[i];
            current_geoloc.description = null;
            current_geoloc.gettingInUsers = new Array();
            current_geoloc.gettingOffUsers = new Array();
            current_geoloc.lat = locationPoint[i].lat();
            current_geoloc.lng = locationPoint[i].lng();
            
            geolocs.push(current_geoloc);
        }
        return MapControl2.AAARoute.createRouteFromGDirectionsByGLocs(geolocs, gDirections, map, vehicle);
};

MapControl2.AAARoute.createRouteFromGDirectionsByGLocs = function(geolocs, gDirections, map, vehicle) 
{
    var route = new Object();
    route.id = -1;
    route.consistent = true;
    
    route.start = geolocs.shift();
    route.end = geolocs.pop();
    route.viaPoints = geolocs;
    route.passengers = null;
    route.polyline = new Array();
    route.vehicle = vehicle.Class; // default vehicle = car
    route.vehicleId = vehicle.Id;
    
    var aaaRoute;
    if( gDirections.getStatus().code == G_GEO_SUCCESS )
    {
        MapControl2.AAARoute.updateRouteModelFromGDirections(route, gDirections);
        
        var thePolyline = gDirections.getPolyline();
        aaaRoute = new MapControl2.AAARoute(route, thePolyline, true, true, map);
    }
    else
    {   
        route.distance = 0; // distanza in km
        route.instructions = new Array();
        route.time = 0;
        aaaRoute = new MapControl2.AAARoute(route, null, true, true, map);
    }
    return aaaRoute;
};

MapControl2.AAARoute.updateRouteModelFromGDirections = function(route, gDirections)
{
        // costruisco le istruzioni di guida
        var d = new Number(parseFloat((gDirections.getDistance()).meters)/1000);
        d.toFixed(3);
        route.distance = d; // distanza in km
        route.instructions = new Array();
        route.time = parseInt(((gDirections.getDuration()).seconds)/60); // duration in seconds
};
// -----------------------------------------------------------------------------------------------

MapControl2.AAARoute.registerClass('MapControl2.AAARoute');

if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();